<!DOCTYPE html>
<html>
<head>
    <title>Electromagnetic Field Diagram</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
        }
        canvas {
            border: 1px solid #ccc;
            background-color: #fff;
        }
    </style>
</head>
<body>
    <canvas id="physicsCanvas" width="600" height="450"></canvas>
    <script>
        const canvas = document.getElementById('physicsCanvas');
        const ctx = canvas.getContext('2d');

        // Helper function to draw an arrow
        function drawArrow(ctx, fromx, fromy, tox, toy, headlen = 10) {
            const dx = tox - fromx;
            const dy = toy - fromy;
            const angle = Math.atan2(dy, dx);
            ctx.beginPath();
            ctx.moveTo(fromx, fromy);
            ctx.lineTo(tox, toy);
            ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
            ctx.moveTo(tox, toy);
            ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
            ctx.stroke();
        }

        // Helper function for text with subscript
        function drawSubscriptText(ctx, text, sub, x, y, font, subScale = 0.7) {
            const mainFontSize = parseFloat(font.match(/(\d+)px/)[1]);
            const subFontSize = mainFontSize * subScale;
            
            ctx.font = font;
            ctx.fillText(text, x, y);
            const textWidth = ctx.measureText(text).width;
            
            ctx.font = `${subFontSize}px sans-serif`;
            ctx.fillText(sub, x + textWidth * 0.9, y + subFontSize * 0.4);
            
            ctx.font = font; // Reset font
        }
        
        // Helper function to draw hatching inside a circular region
        function drawHatching(ctx, cx, cy, radius, spacing = 8, angle = -Math.PI / 4) {
            ctx.save();
            ctx.beginPath();
            ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
            ctx.clip();

            // Draw hatching lines
            ctx.strokeStyle = '#555';
            ctx.lineWidth = 1;
            ctx.beginPath();
            const R = radius * 1.5; // Ensure lines cover the whole circle
            for (let i = -R; i <= R; i += spacing) {
                const x1 = cx - R * Math.cos(angle) + i * Math.sin(angle);
                const y1 = cy - R * Math.sin(angle) - i * Math.cos(angle);
                const x2 = cx + R * Math.cos(angle) + i * Math.sin(angle);
                const y2 = cy + R * Math.sin(angle) - i * Math.cos(angle);
                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
            }
            ctx.stroke();
            ctx.restore();
        }

        // --- Main Drawing ---
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // Parameters
        const radius = 70;
        const d = 80;
        const c1x = 250;
        const c2x = c1x + d;
        const cy = 220;

        // 1. Draw 3D perspective bodies
        const p_len = 250;
        const p_dy = 40;

        // Create gradient for shading
        const gradient = ctx.createLinearGradient(0, cy - radius, 0, cy + radius);
        gradient.addColorStop(0, '#E0E0E0');
        gradient.addColorStop(0.5, '#909090');
        gradient.addColorStop(1, '#E0E0E0');
        ctx.fillStyle = gradient;
        ctx.strokeStyle = 'black';
        ctx.lineWidth = 1.5;

        // Top cylinder body
        ctx.beginPath();
        ctx.moveTo(c1x - 15, cy - radius + 5);
        ctx.lineTo(c1x - 15 + p_len, cy - radius + 5 - p_dy);
        ctx.lineTo(c1x - 15 + p_len, cy + 30 - p_dy);
        ctx.lineTo(c1x - 15, cy + 30);
        ctx.closePath();
        ctx.fill();
        ctx.stroke();

        // Bottom cylinder body
        ctx.beginPath();
        ctx.moveTo(c1x + d - 30, cy - 20);
        ctx.lineTo(c1x + d - 30 + p_len, cy - 20 - p_dy);
        ctx.lineTo(c1x + d - 30 + p_len, cy + radius - 5 - p_dy);
        ctx.lineTo(c1x + d, cy + radius);
        ctx.closePath();
        ctx.fill();
        ctx.stroke();
        
        // 2. Draw the two cross-sections (circles) with hatching
        ctx.strokeStyle = 'black';
        ctx.lineWidth = 2;

        // Left circle
        ctx.fillStyle = 'white';
        ctx.beginPath();
        ctx.arc(c1x, cy, radius, 0, 2 * Math.PI);
        ctx.fill();
        drawHatching(ctx, c1x, cy, radius);
        ctx.beginPath();
        ctx.arc(c1x, cy, radius, 0, 2 * Math.PI);
        ctx.stroke();

        // Right circle
        ctx.fillStyle = 'white';
        ctx.beginPath();
        ctx.arc(c2x, cy, radius, 0, 2 * Math.PI);
        ctx.fill();
        drawHatching(ctx, c2x, cy, radius);
        ctx.beginPath();
        ctx.arc(c2x, cy, radius, 0, 2 * Math.PI);
        ctx.stroke();
        
        // 3. Draw the overlapping region
        // This region is a lens shape formed by the intersection of two circles.
        if (d < 2 * radius) {
            const angle = Math.acos(d / (2 * radius));
            ctx.fillStyle = '#E0E0E0';
            ctx.beginPath();
            // Arc on the left circle
            ctx.arc(c1x, cy, radius, -angle, angle);
            // Arc on the right circle, connecting back to the start
            ctx.arc(c2x, cy, radius, Math.PI - angle, Math.PI + angle, true);
            ctx.closePath();
            ctx.fill();
        }

        // 4. Draw annotations and labels
        ctx.strokeStyle = 'black';
        ctx.fillStyle = 'black';
        ctx.lineWidth = 1.5;

        // Center points O1 and O2
        ctx.beginPath();
        ctx.arc(c1x, cy, 3, 0, 2 * Math.PI);
        ctx.fill();
        ctx.beginPath();
        ctx.arc(c2x, cy, 3, 0, 2 * Math.PI);
        ctx.fill();

        // Text labels O1, O2
        const labelFont = '20px serif';
        drawSubscriptText(ctx, 'O', '1', c1x - 25, cy - 10, labelFont);
        drawSubscriptText(ctx, 'O', '2', c2x + 10, cy - 10, labelFont);

        // Distance 'd'
        const d_y_pos = cy + radius + 30;
        ctx.lineWidth = 1;
        // Vertical guides
        ctx.beginPath();
        ctx.moveTo(c1x, cy);
        ctx.lineTo(c1x, d_y_pos);
        ctx.moveTo(c2x, cy);
        ctx.lineTo(c2x, d_y_pos);
        ctx.stroke();
        // Horizontal line with arrows
        drawArrow(ctx, c1x + 5, d_y_pos, c2x - 5, d_y_pos, 8);
        drawArrow(ctx, c2x - 5, d_y_pos, c1x + 5, d_y_pos, 8);
        ctx.font = 'italic 20px serif';
        ctx.textAlign = 'center';
        ctx.fillText('d', c1x + d / 2, d_y_pos - 8);

        // Currents I
        ctx.font = 'italic 22px serif';
        ctx.textAlign = 'start';
        // Current I on top cylinder (left)
        const I1_x = c1x + 120;
        const I1_y = cy - radius - 5;
        ctx.fillText('I', I1_x + 10, I1_y + 8);
        ctx.lineWidth = 2;
        drawArrow(ctx, I1_x, I1_y, I1_x - 40, I1_y, 10);
        
        // Current I on bottom cylinder (right)
        const I2_x = c2x + 80;
        const I2_y = cy - 20 - 20;
        ctx.fillText('I', I2_x - 10, I2_y + 8);
        drawArrow(ctx, I2_x, I2_y, I2_x + 40, I2_y, 10);
        
        // 5. Draw the caption
        ctx.font = '20px "SimSun", "Songti SC"';
        ctx.textAlign = 'center';
        ctx.fillStyle = 'black';
        ctx.fillText('习题 8-27 图', canvas.width / 2, 400);

    </script>
</body>
</html>